#include "syntax_parser.h"
#include "errors_warnings.h"

#include "main_container.h"
extern MainContainer * CompilerData;

// <field_definition> ~ <field_mods> <trace_part> <data_type> <field_names> ;  {trace, const, public, private, protected, static, int, float, bool, state, time, void, Facility, Storage, Queue, ADTable, Group, OutputStream, NAME}
void Parser::Parse_FieldDefinition(SyntaxTreeNode *parent)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_FieldDefinition");
#endif
	LexicUnit currentUnit;
	// get current lexic unit
	currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

	// if current lexic unit is in the select set of {trace, const, public, private,
	// protected, static, int, float, bool, state, time, void, Facility, Storage,
	// Queue, ADTable, Group, OutputStream, NAME}
	if((currentUnit.type == LUTypeKeyword && 
		(currentUnit.subtype == KeywordConst || currentUnit.subtype == KeywordPublic || currentUnit.subtype == KeywordTrace
		|| currentUnit.subtype == KeywordPrivate || currentUnit.subtype == KeywordProtected
		|| currentUnit.subtype == KeywordStatic || currentUnit.subtype == KeywordInt 
		|| currentUnit.subtype == KeywordFloat || currentUnit.subtype == KeywordBool 
		|| currentUnit.subtype == KeywordState || currentUnit.subtype == KeywordTime 
		|| currentUnit.subtype == KeywordVoid || currentUnit.subtype == KeywordFacility 
		|| currentUnit.subtype == KeywordStorage || currentUnit.subtype == KeywordQueue 
		|| currentUnit.subtype == KeywordADTable || currentUnit.subtype == KeywordGroup 
		|| currentUnit.subtype == KeywordOutputStream))
		|| currentUnit.type == LUTypeIdentifier)
	{
		// write type of production in this node
		parent->Production = Gr_FieldDefinition__FieldMods_TracePart_DataType_FieldNames_spSemiCol;

		// process nonterminal symbol <field_mods> 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		Parse_FieldMods(newNode0);
		
		// <trace_part>
		SyntaxTreeNode * newNode__ = new SyntaxTreeNode();
		parent->Nodes->Add(newNode__);
		Parse_TracePart(newNode__);	

		// process nonterminal symbol <data_type> 
		SyntaxTreeNode * newNode1 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode1);
		Parse_DataType(newNode1);

		// process nonterminal symbol <field_names> 
		SyntaxTreeNode * newNode2 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode2);
		Parse_FieldNames(newNode2);

		// process terminal symbol ';' 
		currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);
		if(currentUnit.type == LUTypeSpecialCharacter && currentUnit.subtype == SpecialCharacterSemicolon)
		{
			SyntaxTreeNode * newNode3 = new SyntaxTreeNode();
			parent->Nodes->Add(newNode3);
			newNode3->LexUnitID=this->CurrentLexicPosition;
			newNode3->Production=Gr_Terminal;
			//increase counter
			CurrentLexicPosition++;
		}
		else
		{
			//report syntactic error
			CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_SEMICOLON_MATCH);
			//increase counter
			CurrentLexicPosition++;
		}
	}
	else
	{
		// ERROR has been found
		// REPORT ERROR
		CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_FIELD_DEFINITION_BEGINING);
		// This is recovoring from error
        this->CurrentLexicPosition++;
	}
#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_FieldDefinition");
#endif
}

// <field_mods> ~ <field_mod> <field_mods>  {const, public, private, protected, static}
// <field_mods> ~ ? {trace, int, float, bool, state, time, void, Facility, Storage, Queue, ADTable, Group, OutputStream, NAME}
// This production is seen as: <field_mods> ~ <field_mod> <field_mod><field_mod>...epsilon
void Parser::Parse_FieldMods(SyntaxTreeNode * parent)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_FieldMods");
#endif
	LexicUnit currentUnit;

	do
	{
		// get current lexic unit
		currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

		// if current lexic unit is in the select set of {const, public, private, protected, static} 
		if(currentUnit.type == LUTypeKeyword && 
			(currentUnit.subtype == KeywordConst || currentUnit.subtype == KeywordPublic 
			|| currentUnit.subtype == KeywordPrivate || currentUnit.subtype == KeywordProtected
			|| currentUnit.subtype == KeywordStatic))
		{
			// write type of production in this node
			parent->Production = Gr_FieldMods__FieldMod_FieldMods;

			// make FieldMod node and add child node to a parent node
			SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
			parent->Nodes->Add(newNode0);
			Parse_FieldMod(newNode0);

		}
		// if current lexic unit is in the select set of {int, float, bool, state, time, void, 
		// Facility, Storage, Queue, ADTable, Group, OutputStream, NAME}
		else if((currentUnit.type == LUTypeKeyword && 
			(currentUnit.subtype == KeywordInt || currentUnit.subtype == KeywordFloat || currentUnit.subtype == KeywordTrace
			|| currentUnit.subtype == KeywordBool || currentUnit.subtype == KeywordState 
			|| currentUnit.subtype == KeywordTime || currentUnit.subtype == KeywordVoid
			|| currentUnit.subtype == KeywordFacility || currentUnit.subtype == KeywordStorage
			|| currentUnit.subtype == KeywordQueue || currentUnit.subtype == KeywordADTable 
			|| currentUnit.subtype == KeywordGroup || currentUnit.subtype == KeywordOutputStream))
			|| currentUnit.type == LUTypeIdentifier)
		{
			// this is epsilon part of production
			parent->Production = Gr_FieldMods__FieldMod_FieldMods;
			// new node is not needed
			// there are no symbols on the right side of this production
			// so that all
			break;
		}
		else
		{
			// ERROR has been found
			// REPORT ERROR
			CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_FIELD_MODS_BEGINING);
			this->CurrentLexicPosition++;
			break;
		}
	}
	while(1);
#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_FieldMods");
#endif
}

// <field_mod> ~ const  {const}
// <field_mod> ~ public  {public}
// <field_mod> ~ private  {private}
// <field_mod> ~ protected  {protected}
// <field_mod> ~ static  {static}
void Parser::Parse_FieldMod(SyntaxTreeNode * parent)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_FieldMod");
#endif
	LexicUnit currentUnit;
	// get current lexic unit
	currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

	// if current lexic unit is in the select set of {const} 
	if(currentUnit.type == LUTypeKeyword && currentUnit.subtype == KeywordConst)
	{
		// write type of production in this node
		parent->Production = Gr_FieldMod__const;
		// process terminal symbol 'const' 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		newNode0->LexUnitID=this->CurrentLexicPosition;
		newNode0->Production=Gr_Terminal;
		//increase counter
		CurrentLexicPosition++;
	}
	// if current lexic unit is in the select set of {public} 
	else if(currentUnit.type == LUTypeKeyword && currentUnit.subtype == KeywordPublic)
	{
		// write type of production in this node
		parent->Production = Gr_FieldMod__public;
		// process terminal symbol 'public' 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		newNode0->LexUnitID=this->CurrentLexicPosition;
		newNode0->Production=Gr_Terminal;
		//increase counter
		CurrentLexicPosition++;
	}
	// if current lexic unit is in the select set of {private} 
	else if(currentUnit.type == LUTypeKeyword && currentUnit.subtype == KeywordPrivate)
	{
		// write type of production in this node
		parent->Production = Gr_FieldMod__private;
		// process terminal symbol 'private' 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		newNode0->LexUnitID=this->CurrentLexicPosition;
		newNode0->Production=Gr_Terminal;
		//increase counter
		CurrentLexicPosition++;
	}
	// if current lexic unit is in the select set of {protected} 
	else if(currentUnit.type == LUTypeKeyword && currentUnit.subtype == KeywordProtected)
	{
		// write type of production in this node
		parent->Production = Gr_FieldMod__protected;
		// process terminal symbol 'protected' 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		newNode0->LexUnitID=this->CurrentLexicPosition;
		newNode0->Production=Gr_Terminal;
		//increase counter
		CurrentLexicPosition++;
	}
	// if current lexic unit is in the select set of {static} 
	else if(currentUnit.type == LUTypeKeyword && currentUnit.subtype == KeywordStatic)
	{
		// write type of production in this node
		parent->Production = Gr_FieldMod__static;
		// process terminal symbol 'static' 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		newNode0->LexUnitID=this->CurrentLexicPosition;
		newNode0->Production=Gr_Terminal;
		//increase counter
		CurrentLexicPosition++;
	}
	else
	{
		// ERROR has been found
		// REPORT ERROR
		CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_FIELD_MOD_BEGINING);
		// This is recovoring from error
        this->CurrentLexicPosition++;
	}
#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_FieldMod");
#endif
}

// <field_names> ~ <field_name> <equals> <field_names_add>  {NAME}
void Parser::Parse_FieldNames(SyntaxTreeNode * parent)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_FieldNames");
#endif
	LexicUnit currentUnit;
	// get current lexic unit
	currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

	// if current lexic unit is in the select set of {NAME} 
	if(currentUnit.type == LUTypeIdentifier)
	{
		// write type of production in this node
		parent->Production = Gr_FieldNames__FieldName_Equals_FieldNamesAdd;

		// process nonterminal symbol <field_name> 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		Parse_FieldName(newNode0);

		// process nonterminal symbol <equals> 
		SyntaxTreeNode * newNode1 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode1);
		Parse_Equals(newNode1);

		// process nonterminal symbol <field_names_add> 
		SyntaxTreeNode * newNode2 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode2);
		Parse_FieldNamesAdd(newNode2);
	}
	else
	{
		// ERROR has been found
		// REPORT ERROR
		CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_FIELD_NAMES_BEGINING);
		// This is recovoring from error
        this->CurrentLexicPosition++;
	}
#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_FieldNames");
#endif
}

// <field_name> ~ NAME <array_definitions>  {NAME}
void Parser::Parse_FieldName(SyntaxTreeNode * parent)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_FieldName");
#endif
	LexicUnit currentUnit;
	// get current lexic unit
	currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

	// if current lexic unit is in the select set of {NAME} 
	if(currentUnit.type == LUTypeIdentifier)
	{
		// write type of production in this node
		parent->Production = Gr_FieldName__NAME_ArrayDefinitions;
		
		// process terminal symbol 'NAME' 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		newNode0->LexUnitID=this->CurrentLexicPosition;
		newNode0->Production=Gr_Terminal;
		//increase counter
		CurrentLexicPosition++;

		// process nonterminal symbol <array_definitions> 
		SyntaxTreeNode * newNode1 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode1);
		Parse_ArrayDefinitions(newNode1);
	}
	else
	{
		// ERROR has been found
		// REPORT ERROR
		CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_FIELD_NAME_BEGINING);
		// This is recovoring from error
        this->CurrentLexicPosition++;
	}
#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_FieldName");
#endif
}

// <field_names_add> ~ , <field_names>  {,}
// <field_names_add> ~ ? {;, )}
void Parser::Parse_FieldNamesAdd(SyntaxTreeNode * parent)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_FieldNamesAdd");
#endif
	LexicUnit currentUnit;
	// get current lexic unit
	currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

	// if current lexic unit is in the select set of {,} 
	if(currentUnit.type == LUTypeSpecialCharacter && currentUnit.subtype == SpecialCharacterComma)
	{
		// write type of production in this node
		parent->Production = Gr_FieldNamesAdd__spComma_FieldNames;
		
		// process terminal symbol ',' 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		newNode0->LexUnitID=this->CurrentLexicPosition;
		newNode0->Production=Gr_Terminal;
		//increase counter
		CurrentLexicPosition++;

		// process nonterminal symbol <field_names> 
		SyntaxTreeNode * newNode1 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode1);
		Parse_FieldNames(newNode1);
	}
	// if current lexic unit is in the select set of {;, )} 
	else if(currentUnit.type == LUTypeSpecialCharacter && 
		(currentUnit.subtype == SpecialCharacterSemicolon || currentUnit.subtype == SpecialCharacterRightParenthesis))
	{
		// this is epsilon part of production
		parent->Production = Gr_FieldNamesAdd__epsilon;
		// new node is not needed
		// there are no symbols on the right side of this production
		// so that all
	}
	else
	{
		// ERROR has been found
		// REPORT ERROR
		CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_FIELD_NAMES_ADD_BEGINING);
		// This is recovoring from error
        this->CurrentLexicPosition++;
	}
#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_FieldNamesAdd");
#endif
}

// <equals> ~ = <expression>  {=}
// <equals> ~ ? {,, ;, )}
void Parser::Parse_Equals(SyntaxTreeNode * parent)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_Equals");
#endif
	LexicUnit currentUnit;
	// get current lexic unit
	currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

	// if current lexic unit is in the select set of {=} 
	if(currentUnit.type == LUTypeOperator && currentUnit.subtype == OperatorAssigment)
	{
		// write type of production in this node
		parent->Production = Gr_Equals__opAssign_Expression;
		// process terminal symbol '=' 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		newNode0->LexUnitID=this->CurrentLexicPosition;
		newNode0->Production=Gr_Terminal;
		//increase counter
		CurrentLexicPosition++;

		// process nonterminal symbol <expression> 
		SyntaxTreeNode * newNode1 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode1);
		Parse_Expression(newNode1);
	}
	// if current lexic unit is in the select set of {,, ;, )} 
	else if(currentUnit.type == LUTypeSpecialCharacter && 
		(currentUnit.subtype == SpecialCharacterComma || currentUnit.subtype == SpecialCharacterSemicolon ||currentUnit.subtype == SpecialCharacterRightParenthesis))
	{
		// this is epsilon part of production
		parent->Production = Gr_Equals__epsilon;
		// new node is not needed
		// there are no symbols on the right side of this production
		// so that all
	}
	else
	{
		// ERROR has been found
		// REPORT ERROR
		CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_EQUALS_BEGINING);
		// This is recovoring from error
        this->CurrentLexicPosition++;
	}
#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_Equals");
#endif
}

// <array_definitions> ~ <array_definition> <array_definitions>  {[}
// <array_definitions> ~ ? {=, ,, ;, )}
// This production is seen as: <array_definitions> ~ <array_definition> <array_definition><array_definition>...epsilon
void Parser::Parse_ArrayDefinitions(SyntaxTreeNode * parent)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_ArrayDefinitions");
#endif
	LexicUnit currentUnit;
	do
	{
		// get current lexic unit
		currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

		// if current lexic unit is in the select set of {[} 
		if(currentUnit.type == LUTypeSpecialCharacter && currentUnit.subtype == SpecialCharacterLeftSquareBracket)
		{
			// write type of production in this node
			parent->Production = Gr_ArrayDefinitions__ArrayDefinition_ArrayDefinitions;
			
			// make ArrayDefinition node and add child node to a parent node
			SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
			parent->Nodes->Add(newNode0);
			Parse_ArrayDefinition(newNode0);

		}
		// if current lexic unit is in the select set of {=, ,, ;, )} 
		else if((currentUnit.type == LUTypeOperator && currentUnit.subtype == OperatorAssigment)
			|| (currentUnit.type == LUTypeSpecialCharacter && 
			(currentUnit.subtype == SpecialCharacterComma || currentUnit.subtype == SpecialCharacterSemicolon || currentUnit.subtype == SpecialCharacterRightParenthesis)))
		{	
			// this is epsilon part of production
			parent->Production = Gr_ArrayDefinitions__ArrayDefinition_ArrayDefinitions;
			// new node is not needed
			// there are no symbols on the right side of this production
			// so that all
			break;
		}
		else
		{
			// ERROR has been found
			// REPORT ERROR
			CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_ARRAY_DEFINITIONS_BEGINING);
			this->CurrentLexicPosition++;
			break;
		}
	}
	while(1);
#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_ArrayDefinitions");
#endif
}

// <array_definition> ~ [ ]  {[}
void Parser::Parse_ArrayDefinition(SyntaxTreeNode * parent)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_ArrayDefinition");
#endif
	LexicUnit currentUnit;
	// get current lexic unit
	currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

	// if current lexic unit is in the select set of {[} 
	if(currentUnit.type == LUTypeSpecialCharacter && currentUnit.subtype == SpecialCharacterLeftSquareBracket)
	{
		// write type of production in this node
		parent->Production = Gr_ArrayDefinition__spLSBrac_spRSBrac;

		// process terminal symbol '[' 
		SyntaxTreeNode * newNode0 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode0);
		newNode0->LexUnitID=this->CurrentLexicPosition;
		newNode0->Production=Gr_Terminal;
		//increase counter
		CurrentLexicPosition++;

		// process terminal symbol ']' 
		currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);
		if(currentUnit.type == LUTypeSpecialCharacter && currentUnit.subtype == SpecialCharacterRightSquareBracket)
		{
			SyntaxTreeNode * newNode1 = new SyntaxTreeNode();
			parent->Nodes->Add(newNode1);
			newNode1->LexUnitID=this->CurrentLexicPosition;
			newNode1->Production=Gr_Terminal;
			//increase counter
			CurrentLexicPosition++;
		}
		else
		{
			//report syntactic error
			CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_RIGHT_SQUARE_BRACKET_MATCH);
			//increase counter
			CurrentLexicPosition++;
		}
	}
	else
	{
		// ERROR has been found
		// REPORT ERROR
		CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_ARRAY_DEFINITION_BEGINING);
		this->CurrentLexicPosition++;
	}
#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_ArrayDefinition");
#endif
}
/*
void Parser::Parse_DataType(SyntaxTreeNode * parent)
{
}*/